using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using Org.Loon.Framework.Xna.Game.Core;
using Org.Loon.Framework.Xna.Game.Action.Scripting.Pack;
using Org.Loon.Framework.Xna.Game.Core.Graphics.OpenGL;
using Org.Loon.Framework.Xna.Game.Core.Graphics;
using Org.Loon.Framework.Xna.Game.Core.Resource;
using Org.Loon.Framework.Xna.Game.Utils.Xml;
using Org.Loon.Framework.Xna.Java.Collections;
using Org.Loon.Framework.Xna.Game.Utils;
using Org.Loon.Framework.Xna.Java;
using Org.Loon.Framework.Xna.Game.Utils.ILog;
using Org.Loon.framework.Xna.Java.Collections;

namespace Org.Loon.Framework.Xna.Game.Action.Scripting
{
    public class ScriptFactory : LRelease
    {

        private PackTileFactory tileFactory;

        private Dictionary<string, Script> scripts;

        private Dictionary<string, LTexturePack> packs;

        private Dictionary<string, PackTileMap> maps;

        private Dictionary<string, PackAnimation> animations;

        private List<Stack<StackFrame>> executionStacks;

        private Callback callback;

        string packName;

        string mapName;

        int packCount;

        private Stream inputStream;

        private Screen screen;

        public ScriptFactory(Stream ins0)
            : this(null, ins0)
        {

        }

        public ScriptFactory(string res)
            : this(null, res)
        {

        }

        public ScriptFactory(Screen screen, string res)
        {
            this.screen = screen;
            try
            {
                this.inputStream = Resources.OpenStream(res);
            }
            catch (IOException e)
            {
                Log.Exception(e);
            }
            this.Init();
        }

        public ScriptFactory(Screen screen, Stream ins0)
        {
            this.screen = screen;
            this.inputStream = ins0;
            this.Init();
        }

        private void Init()
        {
            this.scripts = new Dictionary<string, Script>(10);
            this.packs = new Dictionary<string, LTexturePack>(10);
            this.maps = new Dictionary<string, PackTileMap>(10);
            this.animations = new Dictionary<string, PackAnimation>(10);
            this.executionStacks = new List<Stack<StackFrame>>(10);
        }

        public void SetMapName(string name)
        {
            this.mapName = name;
        }

        public string getMapName()
        {
            return mapName;
        }

        public void setPackName(string name)
        {
            this.packName = name;
        }

        public string getPackName()
        {
            return packName;
        }

        public void Load()
        {
            if (inputStream == null)
            {
                return;
            }
            XMLElement root = XMLParser.Parse(inputStream).GetRoot();
            for (IIterator itr = root.Elements(); itr.HasNext(); )
            {
                Object o = itr.Next();
                if (o is XMLElement)
                {
                    XMLElement ele = (XMLElement)o;
                    if ("packs".Equals(ele.GetName(), StringComparison.InvariantCultureIgnoreCase))
                    {
                        for (IIterator it = ele.Elements("pack"); it.HasNext(); )
                        {
                            LTexturePack pack = new LTexturePack((XMLElement)it
                                    .Next());
                            packs.Add(pack.GetName(), pack);
                            if (packName == null)
                            {
                                packName = pack.GetName();
                            }
                        }
                    }
                    else if ("scripts".Equals(ele.GetName(), StringComparison.InvariantCultureIgnoreCase))
                    {
                        for (IIterator it = ele.Elements("Script"); it.HasNext(); )
                        {
                            XMLElement child = (XMLElement)it.Next();
                            string name = child.GetAttribute("name").GetValue();
                            Script script = new Script(name);
                            for (IIterator it1 = child.Elements(); it1.HasNext(); )
                            {
                                Object obj = it1.Next();
                                if (obj is XMLElement)
                                {
                                    XMLElement child3 = (XMLElement)obj;
                                    if (child3.GetName().Equals("function"))
                                    {
                                        if (child3.HasAttribute("x"))
                                        {
                                            script.Add(new WithPosition(child3
                                                    .GetAttribute("name")
                                                    .GetValue(), child3
                                                    .GetAttribute("x")
                                                    .GetIntValue(), child3
                                                    .GetAttribute("y")
                                                    .GetIntValue()));
                                        }
                                        else
                                        {
                                            if (child3.HasAttribute("text"))
                                            {
                                                script.Add(new WithText(child3
                                                        .GetAttribute("name")
                                                        .GetValue(), child3
                                                        .GetAttribute("text")
                                                        .GetValue()));
                                            }
                                            else
                                            {
                                                script.Add(new UserFunction(child3
                                                        .GetAttribute("name")
                                                        .GetValue()));
                                            }
                                        }
                                    }
                                    if (child3.GetName().Equals("wait"))
                                    {
                                        script
                                                .Add(new WaitFunction(child3
                                                        .GetAttribute("time")
                                                        .GetIntValue()));
                                    }
                                    if (child3.GetName().Equals("jump"))
                                    {
                                        script
                                                .Add(new JumpFunction(child3
                                                        .GetAttribute("script")
                                                        .GetValue()));
                                    }
                                    if (child3.GetName().Equals("execute"))
                                    {
                                        script.Add(new Execute(child3.GetAttribute(
                                                "script").GetValue()));
                                    }
                                    if (child3.GetName().Equals("call"))
                                    {
                                        script
                                                .Add(new CallFunction(child3
                                                        .GetAttribute("script")
                                                        .GetValue()));
                                    }
                                }
                            }
                            scripts.Add(name, script);
                        }
                    }
                    else if ("animations".Equals(ele.GetName(), StringComparison.InvariantCultureIgnoreCase))
                    {
                        string packName = ele.GetAttribute("pack", null);
                        for (IIterator it = ele.Elements("animation"); it.HasNext(); )
                        {
                            XMLElement child = (XMLElement)it.Next();
                            string name = child.GetAttribute("name", "");
                            bool looped = true;
                            if (child.HasAttribute("noloop"))
                            {
                                looped = false;
                            }
                            LTexturePack pack = null;
                            if (packName != null)
                            {
                                pack = (LTexturePack)CollectionUtils.Get(packs, packName);
                            }
                            else
                            {
                                if (packName == null)
                                {
                                    IEnumerator set = packs.Keys.GetEnumerator();
                                    set.MoveNext();
                                    pack = (LTexturePack)CollectionUtils.Get(packs, set.Current);
                                }
                            }
                            string context = child.GetContents();
                            string[] strings = StringUtils.Split(context, ",");
                            PackAnimation animation = new PackAnimation(name,
                                    looped);
                            foreach (string res in strings)
                            {
                                LTexturePack.PackEntry entry = pack.GetEntry(res);
                                if (entry == null)
                                {
                                    try
                                    {
                                        entry = pack
                                                .GetEntry(int.Parse(res));
                                    }
                                    catch (Exception e)
                                    {
                                        Log.Exception(e);
                                    }
                                }
                                if (entry != null)
                                {
                                    animation.AddFrame(new PackFrame(entry));
                                }
                            }
                            animations.Add(name, animation);
                        }
                    }
                    else if ("maps".Equals(ele.GetName(), StringComparison.InvariantCultureIgnoreCase))
                    {
                        for (IIterator it = ele.Elements("map"); it.HasNext(); )
                        {
                            XMLElement child = (XMLElement)it.Next();
                            string name = child.GetAttribute("name",
                                    "" + (JavaRuntime.CurrentTimeMillis()));
                            if (mapName == null)
                            {
                                mapName = name;
                            }
                            maps.Add(name, new PackTileMap(tileFactory, child,
                                    screen));
                        }
                    }
                }
            }
            this.packCount = packs.Count;
            if (inputStream != null)
            {
                try
                {
                    inputStream.Close();
                    inputStream = null;
                }
                catch (Exception e)
                {
                    Log.Exception(e);
                }
            }
        }

        int Update(Stack<StackFrame> stack, StackFrame frame, Callback callback)
        {
            if (frame.IsComplete())
            {
                return Script.COMPLETE_SCRIPT;
            }
            for (; frame.HasNext(); )
            {
                Function command = frame.Next();
                command.Update(this, stack, callback);
            }
            if (frame.HasNext())
            {
                return Script.UNCOMPLETE_FUNCTION;
            }
            return Script.COMPLETE_FUNCTION;
        }

        public void Update(Stack<StackFrame> stack)
        {
            StackFrame frame = (StackFrame)stack.Peek();
            frame.NewTick();
            switch (Update(stack, frame, callback))
            {
                case Script.COMPLETE_SCRIPT:
                    stack.Pop();
                    Update();
                    break;
                case Script.COMPLETE_FUNCTION:
                    break;
                case Script.UNCOMPLETE_FUNCTION:
                    Update();
                    break;
            }
        }

        public void Update()
        {
            for (int i = 0; i < executionStacks.Count; i++)
            {
                Stack<StackFrame> stack = executionStacks[i];
                if (stack.Count == 0)
                {
                    executionStacks.Remove(stack);
                }
                else
                {
                    Update(stack);
                }
            }
        }

        public int GetPackCount()
        {
            return packCount;
        }

        public void SetCallback(Callback callback)
        {
            this.callback = callback;
        }

        internal void Jump(Stack<StackFrame> stack, string name)
        {
            stack.Pop();
            Execute(stack, name);
        }

        internal void Execute(Stack<StackFrame> stack, string name)
        {
            Script script = (Script)CollectionUtils.Get(scripts, name);
            stack.Push(new StackFrame(script));
        }

        public void Call(string name)
        {
            Stack<StackFrame> stack = new Stack<StackFrame>();
            executionStacks.Add(stack);
            Execute(stack, name);
        }

        public void Clear()
        {
            executionStacks.Clear();
        }

        public PackTileFactory GetTileFactory()
        {
            return tileFactory;
        }

        public void SetTileFactory(PackTileFactory tileFactory)
        {
            this.tileFactory = tileFactory;
        }

        public PackAnimation GetAnimation(string name)
        {
            PackAnimation animation = (PackAnimation)CollectionUtils.Get(animations, name);
            if (animation != null)
            {
                return animation;
            }
            LTexturePack.PackEntry entry = ((LTexturePack)CollectionUtils.Get(packs, packName)).GetEntry(name);
            if (entry != null)
            {
                animation = new PackAnimation(name, false);
                animation.AddFrame(new PackFrame(entry));
            }
            else
            {
                try
                {
                    entry = ((LTexturePack)CollectionUtils.Get(packs, packName)).GetEntry(int.Parse(name));
                    if (entry != null)
                    {
                        animation = new PackAnimation(name, false);
                        animation.AddFrame(new PackFrame(entry));
                    }
                }
                catch (Exception)
                {
                }
            }
            if (animation != null)
            {
                animations.Add(name, animation);
            }
            return animation;
        }

        public Screen GetScreen()
        {
            return screen;
        }

        public LTexturePack GetPack(string name)
        {
            return ((LTexturePack)CollectionUtils.Get(packs, name));
        }

        public Script GetScript(string name)
        {
            return ((Script)CollectionUtils.Get(packs, name));
        }

        public PackTileMap GetMap(string name)
        {
            return GetMap(name, null);
        }

        public PackTileMap GetMap(string name, PackTileFactory factory)
        {
            PackTileMap map = ((PackTileMap)CollectionUtils.Get(maps, name));
            if (map != null && map.IsDirty())
            {
                map.Init(factory);
                map.Update();
            }
            return map;
        }

        public void Dispose()
        {
            if (packs != null)
            {
                foreach (LTexturePack pack in packs.Values)
                {
                    if (pack != null)
                    {
                        pack.Dispose();
                    }
                }
                packs.Clear();
            }
            if (scripts != null)
            {
                scripts.Clear();
                scripts = null;
            }
            if (packs != null)
            {
                packs.Clear();
                packs = null;
            }
            if (maps != null)
            {
                maps.Clear();
                maps = null;
            }
            if (animations != null)
            {
                animations.Clear();
                animations = null;
            }
            if (executionStacks != null)
            {
                executionStacks.Clear();
                executionStacks = null;
            }
            if (inputStream != null)
            {
                try
                {
                    inputStream.Close();
                    inputStream = null;
                }
                catch (Exception)
                {
                }
            }
        }

    }
}
